import { httpRequestErrorHandle, networkErrorHandle } from '@/httpRequest/helper/errorHelper'
import store from '@/store'
import { useAuthStore } from '@/store/auth'
import { useEnvStore } from '@/store/env'
import { useGlobalStore } from '@/store/global'
import { reportEventData, safeTrack } from '../../src/dataReport/dataReportUtil'
import aesHelper from './aesHelper'
import { TrackType, trackEvent } from '@/dataReport/tracks'
import { debounce } from 'lodash'

/**
 * Request Interceptors
 */
export type ApiInterceptor<ResponseDataType> = (
  result: Response<ResponseDataType>,
  response: RequestSuccessResult<ResponseDataType>,
  config: RequestConfig
) => Promise<boolean | undefined>

const _interceptors = new Set<ApiInterceptor<any>>()
// Add Interceptor
export function addApiInterceptor<ResponseDataType>(interceptor: ApiInterceptor<ResponseDataType>) {
  _interceptors.add(interceptor)
}
// Remove Interceptor
export function removeApiInterceptor<ResponseDataType>(interceptor: ApiInterceptor<ResponseDataType>) {
  _interceptors.delete(interceptor)
}

export type RequestConfig = UniNamespace.RequestOptions &
  Partial<{
    // 自定义路径
    customUrl: boolean
    // 不检查token
    noAuth: boolean
    // 忽略全局错误提示
    ignoreErrorTips: boolean
  }>
interface RequestSuccessResult<T> extends UniNamespace.RequestSuccessCallbackResult {
  data: Response<T>
}
export interface ResponseStatus {
  code: number
  messageid: string
  msg: string
}
export interface Response<T> extends ResponseStatus, AnyObject {
  data: T
}
// API 错误
export interface ErrorResponse extends ResponseStatus {
  // code: 409
  data: ResponseErrorData
}
export type ResponseErrorData = {
  key: string
  description: string
}
function _generateConfig(config: RequestConfig): RequestConfig {
  const envStore = useEnvStore(store)
  const authStore = useAuthStore(store)
  return {
    // 配置config
    ...config,
    url: config.customUrl || config.url.includes(envStore.host) ? config.url : envStore.host + config.url,
    header: {
      ...config.header,
      Authorization: authStore.accessToken,
      'Content-Type': 'application/json',
    },
  }
}
function _promiseRequest<T, ResponseType = Response<T>>(config: RequestConfig): Promise<ResponseType> {
  return new Promise((resolve, reject) => {
    // 存储请求路径
    let path = config.url
    config = {
      ..._generateConfig(config),
      success: async (res) => {
        const data = res.data as Response<T | ResponseErrorData>
        safeTrack(() => {
          let trackData = {
            request_path: path,
            successOrNot: data.code === 0,
            error_code: data.code,
          }
          //trackEvent('data_request', trackData, TrackType.BusinesssScene)
        })
        // 拦截请求处理
        let intercepted = false
        for (const interceptor of Array.from(_interceptors)) {
          let interceptResult
          try {
            interceptResult = await interceptor((res.data || {}) as Response<T>, res as RequestSuccessResult<T>, config)
          } catch (e) {
            console.error(e)
          }
          if (interceptResult === true) {
            intercepted = true
            break
          }
        }
        if (intercepted) return reject(res.data) // 拦截器拦截后 不再继续执行后续错误处理

        // 通用错误处理
        if (res.statusCode >= 400 && !config.ignoreErrorTips) {
          wx.hideLoading({
            noConflict: true,
            complete: () => {
              uni.showToast({
                duration: 3000,
                title: `【E${res.statusCode}】服务器异常`,
                icon: 'none',
              })
            },
          })
        }

        // Error Code 错误处理
        if (data.code && data.code !== 0 && !config.ignoreErrorTips) {
          httpRequestErrorHandle(res.data as ErrorResponse)
          return reject(res.data)
        }

        resolve(res.data as ResponseType)
      },
      fail: (error) => {
        let failLock = false
        if (!config.ignoreErrorTips) {
          if (!failLock) {
            networkErrorHandle(error)
            failLock = true
          }
        }
        safeTrack(() => {
          let trackData = {
            request_path: path,
            successOrNot: false,
            response_data: error,
          }

          // trackEvent('data_request', trackData, TrackType.BusinesssScene)
        })
        reject(error)
      },
    }

    uni.request(config)
  })
}

export async function request<T, ResponseType = Response<T>>(config: RequestConfig): Promise<ResponseType> {
  const accessable = await checkRequestAccessable(config)

  if (accessable) {
    return _promiseRequest<T, ResponseType>(config)
  } else {
    // 这里是更新token中，排入等待队列的请求（等待token更新完毕后，再执行接口调用）
    return new Promise<ResponseType>(async (resolve, reject) => {
      pendingQueue.add(async () => {
        try {
          // 更新token
          const ret = await _promiseRequest<T, ResponseType>(config)
          resolve(ret)
        } catch (e) {
          // console.error(e)
          reject(e)
        }
      })
    })
  }
}

export function popRequestQueue() {
  for (const requestCallback of pendingQueue) {
    requestCallback()
    pendingQueue.delete(requestCallback)
  }
}

/**
 * Restful API
 */
type RequestData = Record<string, unknown> | unknown[]
export const GetRequest = <T = null, ResponseType = Response<T>>(
  url: string,
  data?: RequestData,
  config?: Partial<RequestConfig>
): Promise<ResponseType> =>
  request({
    url,
    data,
    method: 'GET',
    ...config,
  })

export const PostRequest = <T = null, ResponseType = Response<T>>(
  url: string,
  data?: RequestData,
  config?: Partial<RequestConfig>
): Promise<ResponseType> =>
  request({
    url,
    data,
    method: 'POST',
    ...config,
  })

export const PutRequest = <T = null, ResponseType = Response<T>>(
  url: string,
  data?: RequestData,
  config?: Partial<RequestConfig>
): Promise<ResponseType> =>
  request({
    url,
    data,
    method: 'PUT',
    ...config,
  })

export const DeleteRequest = <T = null, ResponseType = Response<T>>(
  url: string,
  data?: RequestData,
  config?: Partial<RequestConfig>
): Promise<ResponseType> =>
  request({
    url,
    data,
    method: 'DELETE',
    ...config,
  })

/**
 * Token API
 */
// const refreshTokenUrl = '/api/account/getAccountToken'

// 是否正在刷新token
let refreshingToken = false
const pendingQueue = new Set<() => Promise<void>>()

/**
 * 收到api请求后，先拦截，做判断
 * 如果是更新token的请求，不做处理，直接执行
 * 因为只有带token的参数才可以调，所以判断是否在有效期内。如果超出有效期，并且之前没有更新token失败，就将请求放入循环队列；反之，立即执行
 *
 * @param {*} config 请求url,header,method等配置信息
 * @return {Boolean} 返回true，表示立即执行;返回false，表示加入循环队列，稍后执行
 */
async function checkRequestAccessable(config: RequestConfig): Promise<boolean> {
  // 设置不检查token时 跳过检查
  if (config.noAuth === true) return true

  const globalStore = useGlobalStore(store)
  const authStore = useAuthStore(store)

  // const url = config.url

  // 换取token接口
  // if (url.includes(refreshTokenUrl)) return true

  /*
   * needRefresh是否为true，第一次打开小程序或者隐藏过小程序都会变成true
   * 请求更新token，凡是更新失败，小程序生命周期期间后续不再重新尝试，即只尝试一次，后续接口调用如果token失效，走正常错误流程
   */
  if (!authStore.needRefreshToken) return true
  // const expires_time = authStore.accessToken ? uni.getStorageSync('expires_time') : null

  // 是否刷新token中
  if (refreshingToken) return false

  let accessable = true

  // if (expires_time && new Date().getTime() > expires_time + 18 * 3600 * 1000) {
  try {
    // if (globalStore.launchOptions?.query?.phoneData) {
    //   refreshingToken = true
    //   await authStore.loadDecryptedPhone()
    //   await authStore.loadPhoneToken()
    //   accessable = false
    // } else {
    const accessPhoneNumber = authStore.accessPhoneNumber
    if (accessPhoneNumber && accessPhoneNumber.length > 0) {
      refreshingToken = true
      await authStore.loadPhoneToken()
      accessable = false
    } else {
      accessable = true
    }
    // }
  } catch (e) {
    console.error(e)
  } finally {
    refreshingToken = false
    authStore.needRefreshToken = false
  }
  // }

  return accessable
}

export default request
